/// <reference path = "./ShaderComponent.ts" />

module lcc {

export module render {

const {ccclass, property, menu } = cc._decorator;

//@ts-ignore
let gfx = cc.gfx;

/**
 * 值配置
 */
const VC ={
	tag : "value",

	varSelect : true,
	varDeftype : VariableType.UNIFORM,
	varAttrMacro : "ATTR_FLOAT",
	varAttrMacroCheckOnly : false,
	varAttrName : "a_float",
	varUnifMacro : "UNIF_FLOAT",
	varUnifMacroCheckOnly : false,
	varUnifName : "float",
	
	valDefault : 0,
	valNew : ()=>{ return 0; },
	valType : cc.Float,

	typeSize : 1,

	toArray : (arr:number[], value:number, offest:number) => {
		arr[offest] = value;
	}
}

@ccclass("lcc.render.ShaderFloat")
@menu("i18n:lcc-render.menu_component/ShaderFloat")
export class ShaderFloat extends ShaderComponent {

	_tag:string = VC.tag;

	@property(VariableConfig)
	_valueVar:VariableConfig = new VariableConfig(
		VC.varSelect, VC.varDeftype, 
		VC.varAttrMacro, VC.varAttrMacroCheckOnly, VC.varAttrName, 
		VC.varUnifMacro, VC.varUnifMacroCheckOnly, VC.varUnifName);
	@property({
		type : VariableConfig,
		tooltip : "值变量"
	})
	get valueVar(){
		return this._valueVar;
	}
	set valueVar(value:VariableConfig){
		this._valueVar = value;
		this.onRenderUpdateMaterial();
		this.node.emit("shader_update_attribute");
	}
	
	@property({
		type :cc.Enum(ValueType)
	})
	_valueType:ValueType = ValueType.SINGLE;
	@property({
		visible (){
			return this._valueVar.type != VariableType.UNIFORM;
		},
		type :cc.Enum(ValueType),
		tooltip : "值类型"
	})
	get valueType(){
		return this._valueType;
	}
	set valueType(value:ValueType){
		if(this._valueType != value){
			this._valueType = value;
			this.checkVertexValues();
			if(this._valueVar.type == VariableType.ATTRIBUTE){
				this.node.emit("shader_update_vertex");
			}else{
				this.onRenderUpdateMaterial();
			}
		}
	}

	@property(VC.valType)
	_value:typeof VC.valDefault = VC.valNew();
	@property({
		visible (){
			return this._valueType == ValueType.SINGLE;
		},
		type : VC.valType,
		tooltip : "值"
	})
	get value(){
		return this._value;
	}
	set value(value_:typeof VC.valDefault){
		this._value = value_;
		if(this._valueVar.type == VariableType.ATTRIBUTE){
			this.node.emit("shader_update_vertex");
		}else{
			this.onRenderUpdateMaterial();
		}
	}

	@property([VC.valType])
	_vertexValues:typeof VC.valDefault[] = [];
	@property({
		visible (){
			return this._valueType == ValueType.VERTEX;
		},
		type : [VC.valType],
		tooltip : "顶点值"
	})
	get vertexValues(){
		return this._vertexValues;
	}
	set vertexValues(value:typeof VC.valDefault[]){
		this._vertexValues = value;
		this.checkVertexValues();
		if(this._valueVar.type == VariableType.ATTRIBUTE){
			this.node.emit("shader_update_vertex");
		}else{
			this.onRenderUpdateMaterial();
		}
	}

	/**
	 * 值偏移
	 */
    private _valueOffset:number = 0;

    onLoad(){
        this.node.on("render_shape_checked", this.checkVertexValues, this);
        this.node.on("render_update_material", this.onRenderUpdateMaterial, this);
		this.node.on("render_update_attribute", this.onRenderUpdateAttribute, this);
		this.node.on("render_update_vertex", this.onRenderUpdateRenderData, this);
    }

    onDestroy(){
		this.node.targetOff(this);
    }
    
	onEnable(){
        this.onStateUpdate(true);
	}

	onDisable(){
        this.onStateUpdate(false);
    }

    private onStateUpdate(enable:boolean){
		this.node.emit("shader_update_tag");
		if(this._valueVar.type == VariableType.ATTRIBUTE){
			this.node.emit("shader_update_attribute");
		}else{
			//this.node.emit("shader_update_material");
			this.onRenderUpdateMaterial();
		}
		this.checkVertexValues();
	}
	
	/**
	 * 检查顶点值
	 */
	private checkVertexValues(){
		if(this.enabled){
			if(this._valueType == ValueType.VERTEX){
				let rcomp = this.getComponent(RenderSystem);
				if(rcomp){
					let values = this._vertexValues;
					let vc = rcomp._verticesCount;
					for(let i = 0; i < vc; i++){
						let color = values[i];
						if(color == null){
							values[i] = VC.valNew();
						}
					}
					if(values.length > vc){
						this._vertexValues = values.slice(0, vc);
					}
				}
			}else{
				this._vertexValues = [];
			}
		}
	}
    
    private onRenderUpdateMaterial (comp?) {
		comp = comp || this.getComponent(cc.RenderComponent);
		if(comp){
			// make sure material is belong to self.
			let material = comp._materials[0];
			if (material) {
				if (this.checkMaterialMacro(material, this._valueVar.unifMacro)) {
					if(this.enabled && this._valueVar.type === VariableType.UNIFORM){
						this.defineMaterialMacro(material, this._valueVar.unifMacro, true);
						material.setProperty(this._valueVar.unifName, this._value);
						//Editor.log("onRenderUpdateMaterial", this._value);
					}else{
						this.defineMaterialMacro(material, this._valueVar.unifMacro, false);
					}
					//Editor.log("onRenderUpdateMaterial", this.enabled);
				}
			}
		}
    }
	
	/**
	 * 获得属性值
	 * @param comp 
	 */
	private getAttributeValues(comp){
		let values:number[] = [];
		if(this._valueType === ValueType.SINGLE){
			VC.toArray(values, this._value, 0);
		}else{
			let vc = comp._verticesCount;
			for(let i = 0; i < vc; i++){
				VC.toArray(values, this._vertexValues[i] || VC.valNew(), values.length);
			}
		}
		return values;
	}

	private onRenderUpdateAttribute(comp:RenderSystem){
		// @ts-ignore
		let material = comp._materials[0];
		if (material) {
			if (this.checkMaterialMacro(material, this._valueVar.attrMacro)) {
				if(this.enabled && this._valueVar.type === VariableType.ATTRIBUTE){
					this.defineMaterialMacro(material, this._valueVar.attrMacro, true);
					this._valueOffset = comp.addVertexAttribute({ 
						name: this._valueVar.attrName, 
						type: gfx.ATTR_TYPE_FLOAT32, 
						num: VC.typeSize, 
					}, VC.typeSize);
					//Editor.log("onRenderUpdateAttribute", this._valueOffset);
				}else{
					this.defineMaterialMacro(material, this._valueVar.attrMacro, false);
					this._valueOffset = 0;
				}
				//Editor.log("onRenderUpdateAttribute", this.enabled);
			}
		}
	}

	private onRenderUpdateRenderData(comp){
        if(this.enabled){
			//Editor.log("onRenderUpdateRenderData");
			if(this._valueOffset > 0){
				let assembler = comp._assembler;
				let values = this.getAttributeValues(comp);
				let vlen = values.length;
				let valueOffset = this._valueOffset;
				let floatsPerVert = assembler.floatsPerVert;
				let verts = assembler.renderData.vDatas[0];
				for (let i = 0; i < 4; i++) {
					let dstOffset = floatsPerVert * i + valueOffset;
					let srcOffset = VC.typeSize * i;
					if(srcOffset >= vlen){
						srcOffset = 0;
					}
					for(let j = 0; j < VC.typeSize; j++){
						verts[dstOffset + j] = values[srcOffset + j];
					}
				}
			}
        }
	}
}

}

}
